import { Injectable, Logger } from '@nestjs/common';
import { ResourceInfo } from '../types/resource.types';
import * as fs from 'fs';
import * as path from 'path';
import * as https from 'https';
import * as http from 'http';
import { URL } from 'url';
import { Agent } from 'https';
// 使用相对路径，避免模块别名问题
const cdnDomain = process.env.CDN_DOMAIN || 'cdn.appthen.com';

@Injectable()
export class ResourceDownloaderService {
  private readonly logger = new Logger(ResourceDownloaderService.name);
  private readonly maxConcurrent = 10; // 增加到10个并发下载
  private readonly timeout = 60000; // 增加到60秒超时，适应大文件下载
  private readonly debugMode = process.env.NODE_ENV === 'dev'; // 开发环境启用调试
  private readonly requestDelay = 200; // 请求间隔200ms，避免频率过高

  // 创建HTTP连接池以提高性能
  private readonly httpsAgent = new https.Agent({
    keepAlive: true,
    maxSockets: 20,
    maxFreeSockets: 10,
    timeout: this.timeout,
    keepAliveMsecs: 30000,
  });

  private readonly httpAgent = new http.Agent({
    keepAlive: true,
    maxSockets: 20,
    maxFreeSockets: 10,
    timeout: this.timeout,
    keepAliveMsecs: 30000,
  });

  /**
   * 下载所有资源
   */
  async downloadResources(resources: ResourceInfo[], tempDir: string): Promise<{
    downloaded: ResourceInfo[];
    failed: { resource: ResourceInfo; error: string }[];
  }> {
    const downloaded: ResourceInfo[] = [];
    const failed: { resource: ResourceInfo; error: string }[] = [];

    // 分批并发下载，增加批次间延迟避免频率过高
    for (let i = 0; i < resources.length; i += this.maxConcurrent) {
      const batch = resources.slice(i, i + this.maxConcurrent);

      // 为批次内的每个请求添加小延迟，避免同时发起
      const staggeredPromises = batch.map((resource, index) =>
        this.delay(index * this.requestDelay).then(() =>
          this.downloadSingleResource(resource, tempDir)
            .then(result => {
              if (result.success) {
                downloaded.push(result.resource!);
              } else {
                failed.push({ resource, error: result.error! });
              }
            })
            .catch(error => {
              this.logger.error(`下载资源失败: ${resource.originalUrl}`, error);
              failed.push({ resource, error: error.message });
            })
        )
      );

      await Promise.all(staggeredPromises);

      // 批次间增加延迟，避免请求过于密集
      if (i + this.maxConcurrent < resources.length) {
        await this.delay(this.requestDelay * 2);
      }
    }

    this.logger.log(`资源下载完成: 成功 ${downloaded.length}，失败 ${failed.length}`);
    return { downloaded, failed };
  }

  /**
   * 下载单个资源
   */
  private async downloadSingleResource(
    resource: ResourceInfo,
    tempDir: string
  ): Promise<{ success: boolean; resource?: ResourceInfo; error?: string }> {
    const maxRetries = 5; // 增加重试次数
    let lastError: Error | null = null;

    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        const localPath = path.join(tempDir, resource.localPath);
        const localDir = path.dirname(localPath);

        // 确保目录存在
        if (!fs.existsSync(localDir)) {
          fs.mkdirSync(localDir, { recursive: true });
        }

        // 如果文件已存在，跳过下载
        if (fs.existsSync(localPath)) {
          const stats = fs.statSync(localPath);
          resource.size = stats.size;
          this.logger.log(`文件已存在，跳过下载: ${resource.originalUrl}`);
          return { success: true, resource };
        }

        // 构建完整的下载URL
        const downloadUrl = this.buildDownloadUrl(resource.originalUrl);

        this.logger.log(`[尝试 ${attempt}/${maxRetries}] 开始下载: ${resource.originalUrl} -> ${downloadUrl}`);

        // 下载文件
        const buffer = await this.downloadFile(downloadUrl);
        fs.writeFileSync(localPath, buffer);

        // 更新资源信息
        resource.size = buffer.length;
        resource.hash = this.calculateHash(buffer);

        this.logger.log(`下载完成: ${resource.originalUrl} -> ${resource.localPath} (${buffer.length} bytes)`);
        return { success: true, resource };

      } catch (error) {
        lastError = error as Error;
        this.logger.warn(`[尝试 ${attempt}/${maxRetries}] 下载失败: ${resource.originalUrl} - ${error.message}`);

        // 如果不是最后一次尝试，等待一段时间再重试
        if (attempt < maxRetries) {
          const delay = Math.min(2000 * Math.pow(1.5, attempt - 1), 10000); // 优化退避策略，最大10秒
          this.logger.log(`等待 ${delay}ms 后重试...`);
          await this.delay(delay);
        }
      }
    }

    this.logger.error(`下载资源最终失败: ${resource.originalUrl}`, lastError);
    return { success: false, error: lastError?.message || '未知错误' };
  }

  /**
   * 构建完整的下载URL
   */
  private buildDownloadUrl(originalUrl: string): string {
    // 如果已经是完整的URL，直接返回
    if (originalUrl.startsWith('http://') || originalUrl.startsWith('https://')) {
      return originalUrl;
    }

    // 处理相对路径
    if (originalUrl.startsWith('/')) {
      return `https://${cdnDomain}${originalUrl}`;
    }

    // 对于七牛云hash值，添加CDN域名前缀
    // 检查是否为有效的hash格式（15位以上，只包含字母数字下划线连字符）
    if (/^[A-Za-z0-9_-]{15,}$/.test(originalUrl)) {
      return `https://${cdnDomain}/${originalUrl}`;
    }

    // 其他情况，作为相对路径处理
    return `https://${cdnDomain}/${originalUrl}`;
  }

  /**
   * 下载文件内容
   */
  private async downloadFile(url: string): Promise<Buffer> {
    return new Promise((resolve, reject) => {
      const protocol = url.startsWith('https:') ? https : http;
      let urlObj: URL;

      try {
        urlObj = new URL(url);
      } catch (error) {
        reject(new Error(`无效的URL: ${url}`));
        return;
      }

      // 根据URL扩展名确定合适的Accept头
      const acceptHeader = this.getAcceptHeaderByUrl(url);

      const options = {
        hostname: urlObj.hostname,
        port: urlObj.port || (urlObj.protocol === 'https:' ? 443 : 80),
        path: urlObj.pathname + urlObj.search,
        method: 'GET',
        timeout: this.timeout,
        agent: url.startsWith('https:') ? this.httpsAgent : this.httpAgent,
        headers: {
          'Host': urlObj.hostname,
          'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
          'Accept': acceptHeader,
          'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
          'Accept-Encoding': 'gzip, deflate, br',
          'Cache-Control': 'max-age=0',
          'Connection': 'keep-alive',
          'Upgrade-Insecure-Requests': '1',
          'Sec-Fetch-Dest': this.getSecFetchDest(url),
          'Sec-Fetch-Mode': 'no-cors',
          'Sec-Fetch-Site': 'cross-site',
          'Referer': 'https://editor.appthen.com/',
          'DNT': '1'
        }
      };

      const request = protocol.request(options, (response) => {
        // 详细的响应日志（仅在调试模式下）
        if (this.debugMode) {
          this.logger.debug(`下载响应: ${url} - HTTP ${response.statusCode} ${response.statusMessage}`);
          this.logger.debug(`响应头: ${JSON.stringify(response.headers, null, 2)}`);
        }

        // 处理重定向
        if (response.statusCode && (response.statusCode === 301 || response.statusCode === 302 || response.statusCode === 307 || response.statusCode === 308)) {
          const location = response.headers.location;
          if (location) {
            this.logger.log(`重定向到: ${location}`);
            this.downloadFile(location).then(resolve).catch(reject);
            return;
          }
        }

        // 检查状态码
        if (!response.statusCode || response.statusCode < 200 || response.statusCode >= 300) {
          const errorMessage = `HTTP ${response.statusCode}: ${response.statusMessage || 'Unknown Status'}`;
          this.logger.error(`下载失败: ${url} - ${errorMessage}`);
          if (this.debugMode) {
            this.logger.error(`响应头: ${JSON.stringify(response.headers, null, 2)}`);
          }
          reject(new Error(errorMessage));
          return;
        }

        const chunks: Buffer[] = [];
        let totalLength = 0;

        response.on('data', (chunk) => {
          chunks.push(chunk);
          totalLength += chunk.length;
        });

        response.on('end', () => {
          try {
            const buffer = Buffer.concat(chunks);
            if (this.debugMode) {
              this.logger.debug(`下载完成: ${url} - ${buffer.length} bytes`);
            }
            resolve(buffer);
          } catch (error) {
            this.logger.error(`合并缓冲区失败: ${url}`, error);
            reject(error);
          }
        });

        response.on('error', (error) => {
          this.logger.error(`响应流错误: ${url}`, error);
          reject(error);
        });
      });

      request.on('error', (error) => {
        this.logger.error(`请求错误: ${url}`, error);
        reject(error);
      });

      request.on('timeout', () => {
        this.logger.error(`请求超时: ${url} (${this.timeout}ms)`);
        request.destroy();
        reject(new Error(`下载超时: ${url} (${this.timeout}ms)`));
      });

      // 处理连接错误
      request.on('socket', (socket) => {
        socket.on('error', (error) => {
          this.logger.error(`Socket错误: ${url}`, error);
          reject(error);
        });
      });

      if (this.debugMode) {
        this.logger.debug(`发起请求: ${url}`);
      }
      request.end();
    });
  }

  /**
   * 计算文件hash
   */
  private calculateHash(buffer: Buffer): string {
    const crypto = require('crypto');
    return crypto.createHash('md5').update(buffer).digest('hex');
  }

  /**
   * 根据URL获取合适的Accept头
   */
  private getAcceptHeaderByUrl(url: string): string {
    const extension = this.getFileExtension(url).toLowerCase();

    if (['.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg', '.bmp', '.ico'].includes(extension)) {
      return 'image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8';
    } else if (['.js', '.mjs'].includes(extension)) {
      return 'application/javascript,text/javascript,*/*;q=0.1';
    } else if (['.css'].includes(extension)) {
      return 'text/css,*/*;q=0.1';
    } else if (['.woff', '.woff2', '.ttf', '.eot', '.otf'].includes(extension)) {
      return 'font/woff2,font/woff,font/ttf,font/eot,font/otf,application/font-woff,*/*;q=0.1';
    } else if (['.mp4', '.webm'].includes(extension)) {
      return 'video/webp,video/mp4,video/*,*/*;q=0.9';
    } else if (['.mp3', '.wav', '.ogg'].includes(extension)) {
      return 'audio/webp,audio/mp3,audio/wav,audio/ogg,audio/*,*/*;q=0.9';
    } else {
      // 对于七牛云Hash类型的URL，默认使用图片Accept
      return 'image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8';
    }
  }

  /**
   * 获取Sec-Fetch-Dest头
   */
  private getSecFetchDest(url: string): string {
    const extension = this.getFileExtension(url).toLowerCase();

    if (['.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg', '.bmp', '.ico'].includes(extension)) {
      return 'image';
    } else if (['.js', '.mjs'].includes(extension)) {
      return 'script';
    } else if (['.css'].includes(extension)) {
      return 'style';
    } else if (['.woff', '.woff2', '.ttf', '.eot', '.otf'].includes(extension)) {
      return 'font';
    } else {
      return 'empty'; // 对于不明确的类型
    }
  }

  /**
   * 获取文件扩展名
   */
  private getFileExtension(url: string): string {
    try {
      const urlObj = new URL(url);
      const path = urlObj.pathname;
      const lastDot = path.lastIndexOf('.');
      return lastDot > -1 ? path.substring(lastDot) : '';
    } catch {
      // 如果不是完整URL，直接从字符串中提取
      const lastDot = url.lastIndexOf('.');
      return lastDot > -1 ? url.substring(lastDot) : '';
    }
  }

  /**
   * 延迟函数
   */
  private delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}